﻿// Code obtained from http://www.csharp-examples.net/combine-multiple-printdocuments/

using System.Drawing.Printing;
using System.Drawing;
using System.Reflection;
using System.Collections;
using System.Windows.Forms;

namespace FraudMitigator
{
    public class MultiPrintDocument : PrintDocument
    {
        private PrintDocument[] _documents;
        private Font font;
        private string[] stringsToPrint;
        private string stringToPrint;
        private int _docIndex;
        private int strIndex;
        private PrintEventArgs _args;

        // constructors
        public MultiPrintDocument(ArrayList letters)
        {
            font = new Font("Arial", 12);
            _documents = new PrintDocument[letters.Count];
            stringsToPrint = new string[letters.Count];
            int i = 0;
            foreach (Letter letter in letters)
            {
                _documents[i] = new PrintDocument();
                stringsToPrint[i] = letter.text;
                i++;
            }
        }

        public MultiPrintDocument(PrintDocument[] documents)
        {
            _documents = documents;
        }

        // overidden methods
        protected override void OnBeginPrint(PrintEventArgs e)
        {
            base.OnBeginPrint(e);
            if (_documents.Length == 0)
                e.Cancel = true;

            if (e.Cancel) return;

            _args = e;
            _docIndex = 0;  // reset current document index
            CallMethod(_documents[_docIndex], "OnBeginPrint", e);
        }

        protected override void OnQueryPageSettings(QueryPageSettingsEventArgs e)
        {
            e.PageSettings = _documents[_docIndex].DefaultPageSettings;
            
            CallMethod(_documents[_docIndex], "OnQueryPageSettings", e);
            base.OnQueryPageSettings(e);
        }

        protected override void OnPrintPage(PrintPageEventArgs e)
        {
            CallMethod(_documents[_docIndex], "OnPrintPage", e); 
            int charactersOnPage = 0;
            int linesPerPage = 0;

            // Sets the value of charactersOnPage to the number of characters 
            // of stringToPrint that will fit within the bounds of the page.
            e.Graphics.MeasureString(stringsToPrint[strIndex], font,
                e.MarginBounds.Size, StringFormat.GenericTypographic,
                out charactersOnPage, out linesPerPage);

            // Draws the string within the bounds of the page
            e.Graphics.DrawString(stringsToPrint[strIndex], font, Brushes.Black,
                e.MarginBounds, StringFormat.GenericTypographic);

            // Remove the portion of the string that has been printed.
            stringToPrint = stringsToPrint[strIndex].Substring(charactersOnPage);

            // Check to see if more pages are to be printed.
            e.HasMorePages = (stringToPrint.Length > 0);

            base.OnPrintPage(e);
            if (e.Cancel) return;
            if (!e.HasMorePages)
            {
                CallMethod(_documents[_docIndex], "OnEndPrint", _args);
                if (_args.Cancel) return;
                _docIndex++;  // increments the current document index
                strIndex++;

                if (_docIndex < _documents.Length)
                {
                    // says that it has more pages (in others documents)
                    e.HasMorePages = true;
                    CallMethod(_documents[_docIndex], "OnBeginPrint", _args);
                }
            }
        }

        // use reflection to call protected methods of child documents
        private void CallMethod(PrintDocument document, string methodName, object args)
        {
            typeof(PrintDocument).InvokeMember(methodName,
              BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic,
              null, document, new object[] { args });
        }
    }
}